﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MethodSeqGenerator
{
    //An internal class that 
    class SequenceStore
    {
        public List<MethodInvocationHolder> sequence;
        public int frequency = 0;
        public String methodName;
        public String dllName;
        public String typedeclName;
        public bool includeConstructor; //A flag set to false when the sequence does not include constructor or a method call
                                        //for creating the object instance
    }

    //SequenceGroup includes a group of sequences
    class SequenceGroup
    {
        public List<SequenceStore> sequenceGrpList;
        
        public SequenceGroup()
        {
            sequenceGrpList = new List<SequenceStore>();            
        }

        //Returns TRUE if the sequence is added to the list
        //Returns FALSE if the sequence is not added to the list
        public bool AddToSequenceGroup(List<MethodInvocationHolder> methodSequence, bool includeConstructor)
        {        
            List<MethodInvocationHolder> duplSequence = checkForDuplicate(methodSequence);
            if (duplSequence == null)
            {
                //A new sequence added to the group
                SequenceStore ssObj = new SequenceStore();
                ssObj.sequence = methodSequence;
                ssObj.frequency = 1;
                ssObj.dllName = SeqGenerator.currentDLL;
                ssObj.methodName = SeqGenerator.currentMethodName;
                ssObj.typedeclName = SeqGenerator.currentTypeDeclarationName;
                ssObj.includeConstructor = includeConstructor;
                sequenceGrpList.Add(ssObj);
                return true;
            }
            else
            {
                //Make updates to the old sequence in terms of looping constructs
                AdaptLoopingInfo(duplSequence, methodSequence);
            }

            return false;
        }

        //Check for duplicate method sequences. Only exact match is used.
        //Does not even handle the case where method invocations are in different order in
        //the sequence
        private List<MethodInvocationHolder> checkForDuplicate(List<MethodInvocationHolder> methodSequence)
        {
            foreach (SequenceStore seqStore in sequenceGrpList) 
            {
                List<MethodInvocationHolder> seqList = seqStore.sequence;
                if (seqList.Count != methodSequence.Count)
                {
                    continue;
                }

                int size = seqList.Count;
                bool bSame = true;
                for (int tcnt = 0; tcnt < size; tcnt++)
                {
                    if (!seqList[tcnt].Equals(methodSequence[tcnt]))
                    {
                        bSame = false;
                        break;
                    }
                }

                if (bSame)
                {
                    seqStore.frequency++;
                    return seqList;
                }
            }

            return null;
        }

        //Adapts looping from old sequence to new sequence
        private void AdaptLoopingInfo(List<MethodInvocationHolder> oldSequence, List<MethodInvocationHolder> newSequence)
        {
            bool bNewHasLoops = hasLoopConstructs(newSequence);
            if (bNewHasLoops)
            {
                bool bOldHasLoops = hasLoopConstructs(oldSequence);
                if (bOldHasLoops)
                    return;

                for (int tcnt = 0; tcnt < oldSequence.Count; tcnt++)
                    oldSequence[tcnt].nestingDepth = newSequence[tcnt].nestingDepth;
            }
        }

        //Check whether the loop includes looping
        private bool hasLoopConstructs(List<MethodInvocationHolder> sequence)
        {
            int firstNDepth = sequence[0].nestingDepth;

            for (int tcnt = 1; tcnt < sequence.Count; tcnt++)
            {
                if (sequence[tcnt].nestingDepth > firstNDepth)
                    return true;
            }

            return false;
        }
    }
}
